package tool

import (
	"UserServer/api/internal/config"
	"UserServer/model/request"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v4"
	"net"
	"time"
)

// 这一个api 作用就是创捷，解析，获取jwt
// 第一段是创建jwt
// 第二段是解析jwt
// 第三段是获取jwt

var (
	TokenExpired     = errors.New("Token  已过期")
	TokenNotValidYet = errors.New("Token  不可用")
	TokenMalformed   = errors.New("Token  格式错误")
	TokenInvalid     = errors.New("Token  无效")
)

type JWT struct {
	AccessTokenSecret  []byte
	RefreshTokenSecret []byte
}

var c config.Config
var JWTConfig config.Jwt

func init() {
	config.InitConfig(&c)
	JWTConfig = c.Jwt
}

// NewJWT 返回一个jwt实例对象
func NewJWT() *JWT {
	return &JWT{

		AccessTokenSecret:  []byte(JWTConfig.AccessTokenSecret),
		RefreshTokenSecret: []byte(JWTConfig.RefreshTokenSecret),
	}
}

// CreateAccessToken 创建 Access Token
func (j *JWT) CreateAccessToken(req request.JwtAccessClaims) (string, error) {
	// 获取access的过期时间
	ep, _ := ParseDuration(JWTConfig.AccessTokenExpireTime)

	// 获取jwt的claims部分
	claims := request.JwtAccessClaims{
		UUID:   req.UUID,
		UserID: req.UserID,
		RegisteredClaims: jwt.RegisteredClaims{
			Audience:  jwt.ClaimStrings{"TAP"},                // 受众
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(ep)), // 过期时间
			Issuer:    JWTConfig.Issuer,                       // 签名的发行者
		},
	}

	// 创建AccessToken
	AccessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 放回jwt
	return AccessToken.SignedString(j.AccessTokenSecret)
}

// CreateRefreshToken 创建 RefreshToken
func (j *JWT) CreateRefreshToken(req request.JwtRefreshClaims) (string, error) {
	ep, _ := ParseDuration(JWTConfig.RefreshTokenExpireTime)
	claims := request.JwtRefreshClaims{
		UUID: req.UUID,
		RegisteredClaims: jwt.RegisteredClaims{
			Audience:  jwt.ClaimStrings{"TAP"},
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(ep)),
			Issuer:    JWTConfig.Issuer,
		},
	}
	RefreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return RefreshToken.SignedString(j.RefreshTokenSecret)
}

// =====================================================================================================================

// ParseAccessToken 解析 Access Token，验证 Token 并返回 Claims 信息
func (j *JWT) ParseAccessToken(tokenString string) (*request.JwtAccessClaims, error) {
	claims, err := j.parseToken(tokenString, &request.JwtAccessClaims{}, j.AccessTokenSecret) // 解析 Token
	if err != nil {
		return nil, err
	}
	if customClaims, ok := claims.(*request.JwtAccessClaims); ok { // 确保解析出的 Claims 类型正确
		return customClaims, nil
	}
	return nil, TokenInvalid // 如果解析结果无效，返回 TokenInvalid 错误
}

// ParseRefreshToken 解析 Refresh Token，验证 Token 并返回 Claims 信息
func (j *JWT) ParseRefreshToken(tokenString string) (*request.JwtRefreshClaims, error) {
	claims, err := j.parseToken(tokenString, &request.JwtRefreshClaims{}, j.RefreshTokenSecret) // 解析 Token
	if err != nil {
		return nil, err
	}
	if refreshClaims, ok := claims.(*request.JwtRefreshClaims); ok { // 确保解析出的 Claims 类型正确
		return refreshClaims, nil
	}
	return nil, TokenInvalid // 如果解析结果无效，返回 TokenInvalid 错误
}

// parseToken 通用的 Token 解析方法，验证 Token 是否有效并返回 Claims
func (J *JWT) parseToken(tokenString string, claims jwt.Claims, secretKey interface{}) (interface{}, error) {
	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
		return secretKey, nil
	})

	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok { // 处理 Token 验证错误
			switch {
			case ve.Errors&jwt.ValidationErrorMalformed != 0:
				return nil, TokenMalformed // Token 格式错误
			case ve.Errors&jwt.ValidationErrorExpired != 0:
				return nil, TokenExpired // Token 已过期
			case ve.Errors&jwt.ValidationErrorNotValidYet != 0:
				return nil, TokenNotValidYet // Token 还未生效
			default:
				return nil, TokenInvalid // 其他错误返回 Token 无效
			}
		}
		return nil, TokenInvalid // 默认返回 Token 无效错误
	}

	if token.Valid { // 如果 Token 验证通过，返回 Claims
		return token.Claims, nil
	}
	return nil, TokenInvalid // Token 无效，返回错误
}

// =====================================================================================================================

// SetToken 一般是将RefreshToken存入浏览器的cookie当中，而AccessToken则是存入前端的比如js的变量里面
// 传入token 和 过期时间
func SetToken(c *gin.Context, token string, maxAge int) {
	// 获取主机号
	host, _, err := net.SplitHostPort(c.Request.Host)
	if err != nil {
		host = c.Request.Host
	}
	setCookie(c, "x-refresh-token", token, -1, host)
}

// setCookie 设置指定名称和值的cookie
func setCookie(c *gin.Context, name, value string, maxAge int, host string) {
	// 判断host是否是IP地址
	if net.ParseIP(host) != nil {
		// 如果是IP地址，设置cookie的domain为 “/”
		c.SetCookie(name, value, maxAge, "/", "", false, true)
	} else {
		// 如果是域名，设置cookie的domain为域名
		c.SetCookie(name, value, maxAge, "/", host, false, true)
	}
}

// GetAccessToken 从请求头里面提取AccessToken
func GetAccessToken(c *gin.Context) string {
	// 一般AccessToken所在的位置就是请求头里面
	token := c.Request.Header.Get("x-access-token")
	return token
}

// GetRefreshToken 从cookie中提取RefreshToken
func GetRefreshToken(c *gin.Context) string {
	token, _ := c.Cookie("x-refresh-token")
	return token
}

// ClearRefreshToken 清除Refresh Token的cookie
func ClearRefreshToken(c *gin.Context) {
	// 获取请求的host，如果失败则取原始请求host
	host, _, err := net.SplitHostPort(c.Request.Host)
	if err != nil {
		host = c.Request.Host
	}
	// 调用setCookie设置cookie值为空并过期，删除refresh-token
	setCookie(c, "x-refresh-token", "", -1, host)
}

// GetUUID 从Gin的Context中获取JWT解析出来的用户UUID
func GetUUID(c *gin.Context) string {
	// 首先尝试从Context中获取"claims"
	if claims, exists := c.Get("claims"); !exists {
		// 如果不存在，则重新解析Access Token
		token := GetAccessToken(c)

		j := NewJWT()
		claim, err := j.ParseAccessToken(token)
		if err != nil {
			return ""
		}
		return claim.UUID.String()

	} else {
		return claims.(string)
	}
}
